/**
 * @author JGY
 * @since 2024/1
 */
import { KEYBOARD_KEY } from '@app-cad/common/Constants';
import { wsApi } from '@app-cad/server';
import { Box3, Matrix4, Object3D, Raycaster, Vector3 } from 'three-legacy';
import { csEvent } from '../events/EventCenter';
import { Transformer } from './transformer';

import {
  CENTEREVENTS,
  CMEVENTS,
  CSLine,
  css2dInputLable,
  css2dRadiusLabel,
  CSys,
  MathTool,
  Matrix3x4,
  pickedModelData,
  Transform,
  Types,
} from '@amcax/base';
import { Picker3D, PickerMgr, Snaper } from '@amcax/renderer';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { ctx2, dataManager, userEvents } from '@app-cad/module';

class Mover {
  modelsObj: Array<any>;
  modelData: pickedModelData;
  shapeDxid: string;
  sceneSetup;

  init() {
    this.modelsObj = [];
    this.shapeDxid = null;
    this.sceneSetup = ctx2.viewer.sceneSetup;
    this.modelData = null;
  }

  clear() {
    this.modelData?.assisObj?.destroy();
    for (let i = 0, l = this.modelsObj.length; i < l; i++) {
      this.modelsObj[i]?.assisObj?.destroy();
    }
    this.modelsObj = [];
    this.shapeDxid = null;
    this.modelData = null;
  }

  setModelData(holeData: pickedModelData) {
    if (!holeData) return;
    this.shapeDxid = holeData.idInfo.o;
    this.modelData = holeData;
    this.modelsObj.push(holeData.assisObj);
    // 移植后洞未创建实体，仅创建拾取虚体
    // this.modelsObj.push(holeData.originModel.edge);
    // this.modelsObj.push(holeData.originModel.mesh);
    // this.modelsObj.push(holeData.originModel.pickSceneEdge);
    // this.modelsObj.push(holeData.originModel.pickSceneMesh);
  }

  doMove(matrix: Matrix4) {
    for (let i = 0, l = this.modelsObj.length; i < l; i++) {
      this.modelsObj[i].transferByMatrix4(matrix);
    }
  }

  async transformDataCommit(matrix: Matrix4) {
    const newCsys = this.modelData.csys.clone();
    newCsys.applyMatrix4(matrix);
    // 更新placement
    const placement = newCsys.getPlacement();
    dataManager.updatePlacement(this.modelData.idInfo, placement);
    this.sceneSetup.updatePickSenceTexture();
    // 提交transform
    const transform = Transform.matrixToTransform(
      new Matrix3x4().setFromMatrix4(matrix),
    );
    if (transform && this.shapeDxid) {
      await wsApi.setTransform({
        o: this.shapeDxid,
        transform: transform,
      });
    }
  }
}

enum HoleLabel {
  BOTTOM = 0,
  LEFT = 1,
  TOP = 2,
  RIGHT = 3,
}
/**
 * 洞口定位方法实例对象
 * 标注线
 * 输入框
 */
export class LocateHole {
  private static _instance: LocateHole;
  toolsGroup: Object3D;
  workGroup: Object3D;
  sceneSetup;
  leftLabel: css2dRadiusLabel;
  rightLabel: css2dRadiusLabel;
  topLabel: css2dRadiusLabel;
  bottomLabel: css2dRadiusLabel;
  currentLabel: css2dRadiusLabel;
  holeBBox: Box3;
  frameBBox: Box3;
  holeFaceId: string;
  holeId: string;
  holeOriginPos: Vector3;
  mover: Mover;
  tempText: number;

  constructor() {
    this.init();
  }

  public static getInstance(): LocateHole {
    if (!this._instance) {
      this._instance = new LocateHole();
    }
    return this._instance;
  }

  init() {
    this.sceneSetup = ctx2.viewer.sceneSetup;
    this.workGroup = ctx2.viewer.sceneSetup.scene.children[2];
    this.toolsGroup = ctx2.viewer.sceneSetup.scene.children[3];
    this.leftLabel = null;
    this.rightLabel = null;
    this.topLabel = null;
    this.bottomLabel = null;
    this.currentLabel = null;
    this.holeBBox = null;
    this.frameBBox = null;
    this.holeFaceId = null;
    this.holeId = null;
    this.holeOriginPos = new Vector3();
    this.mover = new Mover();
    this.tempText = null;
    document.addEventListener('keyup', this.keyupEvent);
    csEvent().on('updateBlur', this.blurLabel);
    this.mover.init();
  }

  destroy() {
    document.removeEventListener('keyup', this.keyupEvent);
    csEvent().off('updateBlur', this.blurLabel);
    userEvents.off(CMEVENTS.MODEL_REMOVED, this.removeLineLabel);
    if (this.holeId)
      PickerMgr.getPicker<Picker3D>('3d').uncheckModels([this.holeId]);
    if (this.leftLabel) this.leftLabel.destroy();
    if (this.rightLabel) this.rightLabel.destroy();
    if (this.topLabel) this.topLabel.destroy();
    if (this.bottomLabel) this.bottomLabel.destroy();
    if (this.currentLabel) this.currentLabel.destroy();
    this.holeFaceId = null;
    this.holeId = null;
    this.mover.clear();
    LocateHole._instance = null;
    // ot.close();
    ctx2.viewer.requestRender();
  }

  removeLineLabel(data) {
    if (!HoleLocator().holeId) return;
    let needRemove = false;
    for (let i = 0, l = data.length; i < l; i++) {
      if (data[i] === HoleLocator().holeId) {
        needRemove = true;
      }
    }
    if (needRemove) {
      HoleLocator().destroy();
    }
  }

  switchToMover() {
    document.removeEventListener('keyup', this.keyupEvent);
    csEvent().off('updateBlur', this.blurLabel);
    if (this.leftLabel) this.leftLabel.destroy();
    if (this.rightLabel) this.rightLabel.destroy();
    if (this.topLabel) this.topLabel.destroy();
    if (this.bottomLabel) this.bottomLabel.destroy();
    if (this.currentLabel) this.currentLabel.destroy();
    this.holeFaceId = null;
    this.holeId = null;
    LocateHole._instance = null;
    // ot.close();
    ctx2.viewer.requestRender();
  }

  do(holeData: pickedModelData) {
    userEvents.on(CMEVENTS.MODEL_PLACEMENT_CHANGED, this.updateLineLabel);
    userEvents.on(CMEVENTS.MODEL_REMOVED, this.removeLineLabel);
    if (!holeData) return;
    document.addEventListener('keyup', this.keyupEvent);
    this.holeId = holeData.idInfo.o;
    this.holeFaceId = holeData.idInfo.o + ':f' + holeData.idInfo.i;
    this.mover.setModelData(holeData);
    // 获取洞口包围盒
    this.holeBBox = holeData.assisObj.getBBox();
    // 获取父级体块包围盒
    const model = PickerMgr.getPicker<Picker3D>('3d').selectModels([
      holeData.objInfo.parent,
    ]);
    for (const m of model) {
      if (m[1].objInfo.type !== Types.HOLE) {
        this.frameBBox = m[1].assisObj.getBBox();
        m[1].assisObj?.destroy();
        model.delete(m[0]);
      }
    }
    // 调用接口
    wsApi.getHoleInfo(this.holeFaceId).then((res: any): void => {
      if (!res.frame || !res.hole) {
        this.destroy();
        return;
      }
      res.hole.forEach((holeInfo) => {
        if (holeInfo.dxid === this.holeId) {
          this.dealWithHoleInfo(holeInfo);
        }
      });
    });
  }

  dealWithHoleInfo(holeInfo) {
    if (holeInfo.points.length === 0) return;
    const a = new Vector3(
      holeInfo.points[0][0],
      holeInfo.points[0][1],
      holeInfo.points[0][2],
    );
    const b = new Vector3(
      holeInfo.points[1][0],
      holeInfo.points[1][1],
      holeInfo.points[1][2],
    );
    const c = new Vector3(
      holeInfo.points[2][0],
      holeInfo.points[2][1],
      holeInfo.points[2][2],
    );
    const d = new Vector3(
      holeInfo.points[3][0],
      holeInfo.points[3][1],
      holeInfo.points[3][2],
    );

    if (this.checkPoints(a, b, c, d)) return;

    const vecNum = new Vector3();
    vecNum.add(a);
    vecNum.add(b);
    vecNum.add(c);
    vecNum.add(d);
    const center = vecNum.clone().multiplyScalar(0.25);
    const o1 = a.clone().add(b.clone()).multiplyScalar(0.5);
    const o2 = b.clone().add(c.clone()).multiplyScalar(0.5);
    const o3 = c.clone().add(d.clone()).multiplyScalar(0.5);
    const o4 = d.clone().add(a.clone()).multiplyScalar(0.5);
    this.drawDistanceLine(this.getRayDir(o1, center), o1, HoleLabel.TOP);
    this.drawDistanceLine(this.getRayDir(o2, center), o2, HoleLabel.BOTTOM);
    this.drawDistanceLine(this.getRayDir(o3, center), o3, HoleLabel.LEFT);
    this.drawDistanceLine(this.getRayDir(o4, center), o4, HoleLabel.RIGHT);
  }

  checkPoints(a, b, c, d) {
    let shouldDrop = false;
    const p = [a, b, c, d];
    for (let i = 0; i < 4; i++) {
      for (let j = i + 1; j < 4; j++) {
        if (this.areVectorsEqual(p[i], p[j])) {
          shouldDrop = true;
        }
      }
    }
    return shouldDrop;
  }

  areVectorsEqual(v1, v2, tolerance = 0.0001) {
    return (
      Math.abs(v1.x - v2.x) <= tolerance &&
      Math.abs(v1.y - v2.y) <= tolerance &&
      Math.abs(v1.z - v2.z) <= tolerance
    );
  }

  getRayDir(origin: Vector3, center: Vector3) {
    const rayDir = origin.clone().sub(center.clone()).normalize();
    return rayDir;
  }

  drawDistanceLine(rayDir: Vector3, origin: Vector3, current: number) {
    // 圆形洞口误差处理 0.002
    const l = this.holeBBox.max.clone().sub(this.holeBBox.min.clone()).length();
    const o = rayDir
      .clone()
      .setLength(0.002 * l)
      .add(origin.clone());
    const ray = new Raycaster(o, rayDir);
    const edgeScene = this.workGroup.children[3];
    const hits = ray.intersectObject(edgeScene, true);
    let verticalPoint;
    let hitHole = false;
    for (const hit of hits) {
      if (
        !hitHole &&
        hit.distance > 0.01 &&
        hit.object.type === 'LineSegments'
      ) {
        verticalPoint = hit.point;
        hitHole = true;
      }
    }

    // 判断点是否在包围盒内部
    if (this.frameBBox && verticalPoint) {
      if (!this.frameBBox.containsPoint(verticalPoint)) {
        console.error('超出了', verticalPoint);
        return;
      }
    } else {
      return;
    }

    // if (!verticalPoint) {
    //   return
    // }

    const length = origin.distanceTo(verticalPoint);
    if (current === HoleLabel.TOP) {
      if (!this.topLabel) {
        this.topLabel = this.createLineLabel(origin, verticalPoint, length);
        this.topLabel.addTo(this.toolsGroup);
      } else {
        this.topLabel.updateLine(origin, verticalPoint, length);
      }
    } else if (current === HoleLabel.BOTTOM) {
      if (!this.bottomLabel) {
        this.bottomLabel = this.createLineLabel(origin, verticalPoint, length);
        this.bottomLabel.addTo(this.toolsGroup);
      } else {
        this.bottomLabel.updateLine(origin, verticalPoint, length);
      }
    } else if (current === HoleLabel.LEFT) {
      if (!this.leftLabel) {
        this.leftLabel = this.createLineLabel(origin, verticalPoint, length);
        this.leftLabel.addTo(this.toolsGroup);
      } else {
        this.leftLabel.updateLine(origin, verticalPoint, length);
      }
    } else if (current === HoleLabel.RIGHT) {
      if (!this.rightLabel) {
        this.rightLabel = this.createLineLabel(origin, verticalPoint, length);
        this.rightLabel.addTo(this.toolsGroup);
      } else {
        this.rightLabel.updateLine(origin, verticalPoint, length);
      }
    }
    ctx2.viewer.requestRender();
  }

  createLineLabel(start, end, text) {
    const attrs = {
      start: start,
      end: end,
      text: text,
    };
    const lineLabel = new css2dRadiusLabel(attrs);
    lineLabel.line.setLineWidth(2);
    lineLabel.changeColor(0x000000);
    lineLabel.label.clickFocus();
    return lineLabel;
  }

  // 洞口移动后更新定位线
  updateLineLabel = () => {
    if (!this.holeFaceId || !this.holeId) {
      this.destroy();
      return;
    }
    wsApi.getHoleInfo(this.holeFaceId).then((res: any): void => {
      if (!res.frame || !res.hole) {
        this.destroy();
        return;
      }
      res.hole.forEach((holeInfo) => {
        if (holeInfo.dxid === this.holeId) {
          this.dealWithHoleInfo(holeInfo);
        }
      });
    });
    setTimeout(() => {
      if (this.holeId)
        PickerMgr.getPicker<Picker3D>('3d').selectModels([this.holeId]);
    }, 250);
    this.blurLabel();
    this.sceneSetup.updatePickSenceTexture();
  };

  blurLabel = () => {
    if (this.leftLabel) {
      this.leftLabel.updataText();
      this.leftLabel.blur();
    }
    if (this.topLabel) {
      this.topLabel.updataText();
      this.topLabel.blur();
    }
    if (this.rightLabel) {
      this.rightLabel.updataText();
      this.rightLabel.blur();
    }
    if (this.bottomLabel) {
      this.bottomLabel.updataText();
      this.bottomLabel.blur();
    }
  };

  updateFocus() {
    if (
      !this.leftLabel ||
      !this.rightLabel ||
      !this.topLabel ||
      !this.bottomLabel
    )
      return;
    const lf = this.leftLabel.label.onfocus;
    const rf = this.rightLabel.label.onfocus;
    const tf = this.topLabel.label.onfocus;
    const bf = this.bottomLabel.label.onfocus;
    if (!lf && !rf && !tf && !bf) {
      this.leftLabel.focus();
    } else if (lf && !rf && !tf && !bf) {
      this.leftLabel.updataText();
      this.leftLabel.blur();
      this.topLabel.focus();
    } else if (!lf && !rf && tf && !bf) {
      this.topLabel.updataText();
      this.topLabel.blur();
      this.rightLabel.focus();
    } else if (!lf && rf && !tf && !bf) {
      this.rightLabel.updataText();
      this.rightLabel.blur();
      this.bottomLabel.focus();
    } else if (!lf && !rf && !tf && bf) {
      this.bottomLabel.updataText();
      this.bottomLabel.blur();
      this.leftLabel.focus();
    }
  }

  getFocusLabel() {
    let label = null;
    if (this.leftLabel && this.leftLabel.label.onfocus) {
      label = this.leftLabel;
    }
    if (this.rightLabel && this.rightLabel.label.onfocus) {
      label = this.rightLabel;
    }
    if (this.topLabel && this.topLabel.label.onfocus) {
      label = this.topLabel;
    }
    if (this.bottomLabel && this.bottomLabel.label.onfocus) {
      label = this.bottomLabel;
    }
    return label;
  }

  abort() {
    const dir = this.currentLabel.line.getWorldNormal();
    const transPos = dir
      .clone()
      .setLength(MathTool.fixNumber(this.tempText, 6));
    const matrix = new Matrix4().makeTranslation(
      transPos.x,
      transPos.y,
      transPos.z,
    );
    this.mover.doMove(matrix);
    this.transformDataCommit(matrix);
  }

  transformDataCommit(matrix) {
    this.mover.transformDataCommit(matrix);
    // ctx.cmdManager.execute(new ChaosTransaction());
  }

  locate() {
    this.currentLabel = this.getFocusLabel();
    if (!this.currentLabel) return;
    const getText = this.currentLabel.getText();
    // if (getText <= 0) return;
    const text = +getText;
    const length =
      this.currentLabel.line.getLength() - MathTool.fixNumber(text, 6);

    const dir = this.currentLabel.line.getWorldNormal();
    const transVec = dir.clone().setLength(length);
    const matrix = new Matrix4().makeTranslation(
      transVec.x,
      transVec.y,
      transVec.z,
    );
    // this.mover.doMove(matrix);
    // this.transformDataCommit(matrix);

    if (getText <= 0) this.currentLabel.destroy();

    /// ============ 输入数值过大,洞口在体块外部 =============

    const holePreview = new Box3();
    holePreview.min = this.holeBBox.clone().min.add(transVec.clone());
    holePreview.max = this.holeBBox.clone().max.add(transVec.clone());
    if (
      !this.frameBBox.containsBox(holePreview) &&
      !this.frameBBox.intersectsBox(holePreview)
    ) {
      this.currentLabel.updataText();
      // ot.error('脱离体块，无法生成');
      MessageTip.error('脱离体块，无法生成');
      return;
    } else {
      this.mover.doMove(matrix);
      this.transformDataCommit(matrix);
      // ot.close()
    }
  }

  keyupEvent = (e) => {
    e.preventDefault();
    // e.stopPropagation()
    if (e.key === KEYBOARD_KEY.TAB) {
      this.updateFocus();
    }
    if (e.key === KEYBOARD_KEY.ENTER || e.key === KEYBOARD_KEY.SPACE) {
      this.locate();
    }
    if (e.key === KEYBOARD_KEY.ESC) {
      this.destroy();
    }
    if (e.key === KEYBOARD_KEY.DELETE) {
      /**/
    }
  };
}

/**
 * 洞口定位线方法
 */
export const HoleLocator = () => {
  return LocateHole.getInstance();
};

// =================================
//      洞口自由移动
// =================================
enum Step {
  MOVESTART = 'moveStart',
  MOVEEND = 'moveEnd',
}
/**
 * 洞口移动方法实例对象
 */
export class MoveHole {
  private static _instance: MoveHole;
  scene;
  toolsGroup;
  sceneSetup;
  holeDatas: Map<string, pickedModelData>;
  holeId: string;
  holeBBox: Box3;
  blockBBox: Box3;
  step: string;
  hoverPointVec: Vector3;
  startPoint: Vector3;
  endPoint: Vector3;
  lineLabel: css2dInputLable;
  lineObj: CSLine;
  length: number;
  matrix: Matrix4; // 偏移矩阵
  previousInverseMatrix: Matrix4; // 上次的偏移矩阵的逆
  abortMatrix: Matrix4; // 上次的偏移矩阵
  finalMatrix: Matrix4; // 最终偏移矩阵
  position: Vector3; // 初始坐标
  previousPosition: Vector3; // 上次偏移后的坐标
  deltaPosition: Vector3; // 偏移量
  csys: CSys; // 初始csys
  deltaOrigin: Vector3; // 偏移origin
  csysMatrix: Matrix4; // transform偏移矩阵
  illegal: boolean; // 是否合法，可以放置
  basePlaneNormal: Vector3; // 洞口基准平面，不捕捉特征点的情况下洞口的移动将会被限制在基准面
  ptOnPlane: Vector3; // 基准平面上一基准点
  checkBox: Box3;

  constructor() {
    this.init();
  }

  public static getInstance(): MoveHole {
    if (!this._instance) {
      this._instance = new MoveHole();
    }
    return this._instance;
  }

  init() {
    this.toolsGroup = ctx2.viewer.sceneSetup.scene.children[3];
    this.sceneSetup = ctx2.viewer.sceneSetup;
    this.startPoint = null;
    this.endPoint = null;
    this.step = Step.MOVESTART;
    this.holeDatas = new Map();
    this.holeBBox = new Box3();
    this.blockBBox = new Box3();
    this.checkBox = new Box3();
    this.matrix = new Matrix4();
    this.previousInverseMatrix = new Matrix4();
    this.abortMatrix = new Matrix4();
    this.finalMatrix = new Matrix4();
    this.csysMatrix = new Matrix4();
    this.deltaOrigin = new Vector3();
    this.position = new Vector3();
    this.previousPosition = new Vector3();
    this.deltaPosition = new Vector3();
    this.length = 0;
    this.hoverPointVec = null;
    this.illegal = true;
    this.basePlaneNormal = new Vector3();
    this.ptOnPlane = new Vector3();
    this.holeId = null;
  }

  end() {
    this.startPoint = null;
    this.endPoint = null;
    this.hoverPointVec = null;
    this.step = Step.MOVESTART;
    this.holeBBox = new Box3();
    this.blockBBox = new Box3();
    this.checkBox = new Box3();
    if (this.lineObj) this.lineObj.destroy();
    if (this.lineLabel) this.lineLabel.destroy();
    this.lineObj = null;
    this.lineLabel = null;
    this.length = 0;
    if (this.holeDatas) {
      for (const model of this.holeDatas) {
        model[1].assisObj?.destroy();
      }
      this.holeDatas.clear();
    }
    PickerMgr.getPicker<Picker3D>('3d').initPickedInfo();
    Transformer().clear();
    this.matrix = new Matrix4();
    this.previousInverseMatrix = new Matrix4();
    this.abortMatrix = new Matrix4();
    this.position = new Vector3();
    this.previousPosition = new Vector3();
    this.holeId = null;
    Snaper.clear();
    ctx2.viewer.requestRender();
    // this.sceneSetup.updatePickSenceTexture();
  }

  abort() {
    // this.showCurrentModelPickPoint();
    this.startPoint = null;
    this.endPoint = null;
    this.step = Step.MOVESTART;
    if (this.lineObj) this.lineObj.destroy();
    if (this.lineLabel) this.lineLabel.destroy();
    this.lineObj = null;
    this.lineLabel = null;
    this.length = 0;
    this.position = new Vector3();
    this.previousPosition = new Vector3();
    Transformer().doTransform(
      this.holeDatas,
      this.abortMatrix.clone().multiply(this.previousInverseMatrix),
    );
    // 移动包围盒
    // const boxs = Picker.getSelectedGroupBoxs();
    // Transformer().applyGroupBoxs(boxs, this.abortMatrix.clone().multiply(this.previousInverseMatrix));
    this.matrix = new Matrix4();
    this.previousInverseMatrix = new Matrix4();
    this.abortMatrix = new Matrix4();
    Snaper.clear();
    ctx2.viewer.requestRender();
    // ot.info('点击确认移动起始点，Esc退出');
  }

  continues() {
    // this.showCurrentModelPickPoint();
    this.startPoint = null;
    this.endPoint = null;
    this.step = Step.MOVESTART;
    if (this.lineObj) this.lineObj.destroy();
    if (this.lineLabel) this.lineLabel.destroy();
    this.lineObj = null;
    this.lineLabel = null;
    this.length = 0;
    this.position = this.previousPosition;
    this.previousPosition = new Vector3();
    this.abortMatrix = this.matrix;
    Transformer().commit(this.csysMatrix);
    this.holeBBox = this.checkBox.clone();
    Snaper.clear();
    ctx2.viewer.requestRender();
    // ot.info('点击确认移动起始点，Esc退出');
  }

  // =================== 方法函数 =====================

  doMove(
    holeDatas: Map<string, pickedModelData>,
    normal: Vector3,
    ptOnPlane: Vector3,
  ) {
    this.basePlaneNormal = normal;
    this.ptOnPlane = ptOnPlane;
    this.holeDatas = holeDatas;
    for (const h of holeDatas) {
      this.holeBBox = h[1].assisObj.getBBox();
      this.holeId = h[1].idInfo.o;
      const model = PickerMgr.getPicker<Picker3D>('3d').selectModels([
        h[1].objInfo.parent,
      ]);
      for (const m of model) {
        if (m[1].objInfo.type !== Types.HOLE) {
          this.blockBBox = m[1].assisObj.getBBox();
          m[1].assisObj?.destroy();
          model.delete(m[0]);
        }
      }
    }

    this.enter();
  }

  pickPoint(e) {
    // if (this.step === Step.MOVESTART) {
    //   obj = Picker.pick(e, { line: true, face: true })
    //   if (obj?.idInfo?.t === PickType.PLANE) {
    //     const faces = obj.modelInfo.modelData.faces;
    //     for (let i = 0, l = faces.length; i < l; i++) {
    //       if (i === obj.idInfo.i) {
    //         const csys = obj.assisObj.csys
    //         const placeMent = csys.getPlacement()
    //         const planeNormal = new Vector3(placeMent[1][0], placeMent[1][1], placeMent[1][2]).normalize()
    //         // this.basePlaneNormal = planeNormal
    //       }
    //     }
    //   } else {
    //     // this.basePlaneNormal = new Vector3(0, 0, 1)
    //   }
    // } else {
    //   obj = Picker.pick(e, { line: true })
    // }
    // pick point
    let nowTakenPoint;
    const obj = PickerMgr.getPicker<Picker3D>('3d').pick(e, { line: true });
    if (obj) {
      nowTakenPoint = Snaper.snapKeyPoint(e, { lineCross: false });
    }
    if (nowTakenPoint?.vector) {
      this.hoverPointVec = nowTakenPoint.vector;
      if (
        !this.isPointInPlane(
          this.hoverPointVec,
          this.ptOnPlane,
          this.basePlaneNormal,
        )
      ) {
        this.hoverPointVec = null;
      }
    } else {
      this.hoverPointVec = null;
      Snaper.clear();
    }
  }

  isPointInPlane(point: Vector3, pointOnPlane: Vector3, normal: Vector3) {
    const num = pointOnPlane.clone().sub(point.clone()).dot(normal.clone());
    if (num === 0) {
      return true;
    } else {
      return false;
    }
  }

  checkLegality(moveDir, distance) {
    this.checkBox = this.holeBBox.clone();
    this.checkBox.min = moveDir
      .clone()
      .setLength(distance)
      .add(this.holeBBox.min.clone());
    this.checkBox.max = moveDir
      .clone()
      .setLength(distance)
      .add(this.holeBBox.max.clone());
    if (
      !this.blockBBox.containsBox(this.checkBox) &&
      !this.blockBBox.intersectsBox(this.checkBox)
    ) {
      for (const h of this.holeDatas) {
        h[1].assisObj.setMeshColor(0xff0000);
        h[1].assisObj.setMeshOpacity(0.2);
        // h[1].assisObj.setEdgeColor(0xff0000)
      }
      this.illegal = true;
      // ot.error('已脱离体块，无法生成')
    } else {
      for (const h of this.holeDatas) {
        h[1].assisObj.setDefaultColor();
        h[1].assisObj.setDefaultOpacity();
      }
      this.illegal = false;
      // ot.info('点击确认移动终点')
    }
  }

  // 更新重新渲染导致model对象失效的所选model
  updateModels = (data) => {
    setTimeout(() => {
      const picker = PickerMgr.getPicker<Picker3D>('3d');
      picker.selectModels(data);
      const hole = picker.getSelectedModels();
      this.holeDatas = new Map(hole);
      this.sceneSetup.updatePickSenceTexture();
    }, 250);
    // // Picker.selectModels([this.holeId]);
    // Picker.selectModels(data);
    // const hole = Picker.getSelectedModels();
    // console.log('hole+++++++++++++', hole)
    // // for (const h of hole) {
    // //   if (h[1].idInfo.meta !== Metas.HOLE) {
    // //     h[1].assisModel?.destroy();
    // //     hole.delete(h[0]);
    // //   }
    // //   if (h[0] !== this.holeId) {
    // //     h[1].assisModel?.destroy();
    // //     hole.delete(h[0]);
    // //   }
    // // }
    // this.holeDatas = new Map(hole);
    // this.sceneSetup.updatePickSenceTexture();
    // // ctx2.viewer.requestRender();
  };

  hideCurrentModelPickPoint() {
    if (!this.holeDatas) return;
    for (const holeData of this.holeDatas) {
      holeData[1].originModel.pickSceneEdge.visible = false;
      holeData[1].originModel.pickSceneMesh.visible = false;
    }
    this.sceneSetup.updatePickSenceTexture();
  }

  showCurrentModelPickPoint() {
    if (!this.holeDatas) return;
    for (const holeData of this.holeDatas) {
      holeData[1].originModel.pickSceneEdge.visible = true;
      holeData[1].originModel.pickSceneMesh.visible = true;
    }
    this.sceneSetup.updatePickSenceTexture();
  }

  checkStandardAxis() {
    const x = this.basePlaneNormal.x;
    const y = this.basePlaneNormal.y;
    const z = this.basePlaneNormal.z;
    if (
      ((x === 1 || x === -1) && y === 0 && z === 0) ||
      ((y === 1 || y === -1) && x === 0 && z === 0) ||
      ((z === 1 || z === -1) && y === 0 && x === 0)
    ) {
      return true;
    }
    return false;
  }

  drawLabelLine(e) {
    if (!this.lineObj) {
      const attrs = {
        vertexs: [this.startPoint, this.endPoint],
      };
      this.lineObj = new CSLine(attrs);
      this.lineObj.addTo(this.toolsGroup);
      ctx2.viewer.requestRender();
    } else {
      this.lineObj.setColor(0x2c2c2c);
      this.lineObj.hidePoints();
      this.lineObj.setVertexs([this.startPoint, this.endPoint]);
      // console.log('normal', this.basePlaneNormal)
      // 基准面为标准平面时，坐标轴吸附
      if (MathTool.isParallelToStandardAxis(this.basePlaneNormal)) {
        const mouseV = this.sceneSetup.mouseOnPlane(
          e.mouse.x,
          e.mouse.y,
          this.basePlaneNormal,
          this.ptOnPlane.clone(),
        );
        const axisSnapRes = Snaper.asixSnapByVecs({
          line: this.lineObj,
          startVector: this.startPoint.clone(),
          curVector: mouseV.clone(),
          tolerance: 12,
        });
        if (axisSnapRes) {
          // this.endPoint = this.hoverPointVec ? this.hoverPointVec : axisSnapRes.endP
          this.endPoint = this.hoverPointVec
            ? this.hoverPointVec
            : MathTool.moduleProgression(this.startPoint, axisSnapRes.endP, 10);
          this.lineObj.setVertexs([this.startPoint, this.endPoint]);
        }
      }

      const labelPosition = new Vector3(
        (this.startPoint.x + this.endPoint.x) / 2,
        (this.startPoint.y + this.endPoint.y) / 2,
        (this.startPoint.z + this.endPoint.z) / 2,
      );
      this.length = this.startPoint.distanceTo(this.endPoint);
      if (!this.lineLabel) {
        this.lineLabel = new css2dInputLable({
          text: this.length,
          position: labelPosition,
        });
        this.lineLabel.focus();
        this.lineLabel.addTo(this.toolsGroup);
      } else {
        this.lineLabel.update(this.length, labelPosition);
      }

      ctx2.viewer.requestRender();
    }
  }

  calculateTransform() {
    const moveDir = this.endPoint
      .clone()
      .sub(this.startPoint.clone())
      .normalize();
    const distance = this.startPoint.clone().distanceTo(this.endPoint.clone());
    // 计算偏移矩阵并应用
    this.deltaPosition = moveDir
      .clone()
      .setLength(distance)
      .add(this.position.clone());
    this.previousPosition = this.deltaPosition.clone();
    if (distance > 1000000) return;
    this.matrix = new Matrix4().makeTranslation(
      this.deltaPosition.x,
      this.deltaPosition.y,
      this.deltaPosition.z,
    );
    this.finalMatrix = this.matrix
      .clone()
      .multiply(this.previousInverseMatrix.clone());
    // this.applyMatrix4(this.finalMatrix);
    Transformer().doTransform(this.holeDatas, this.finalMatrix);
    this.previousInverseMatrix = this.matrix.clone().invert();
    // 计算transform偏移矩阵
    this.deltaOrigin = moveDir.clone().setLength(distance);
    this.csysMatrix = new Matrix4().makeTranslation(
      this.deltaOrigin.x,
      this.deltaOrigin.y,
      this.deltaOrigin.z,
    );

    // 检测合法性
    this.checkLegality(moveDir, distance);

    // 移动包围盒
    // const boxs = Picker.getSelectedGroupBoxs();
    // Transformer().applyGroupBoxs(boxs, this.finalMatrix);
  }

  // =================== pointer事件 =====================

  pointerDown = (e) => {
    if (!e.button.onLeft) return;
  };

  pointerUp = (e) => {
    if (!e.button.onLeft) return;

    if (this.step === Step.MOVESTART) {
      const point = this.hoverPointVec
        ? this.hoverPointVec
        : this.sceneSetup.mouseOnPlane(
            e.mouse.x,
            e.mouse.y,
            this.basePlaneNormal,
            this.ptOnPlane.clone(),
          );
      this.startPoint = new Vector3(point.x, point.y, point.z);
      this.step = Step.MOVEEND;
      // this.hideCurrentModelPickPoint();
      // ot.info('点击确认移动终点或输入数值按Space/Enter确认，Esc取消');
    } else if (this.step === Step.MOVEEND) {
      if (!this.illegal) {
        this.continues();
      } else {
        for (const h of this.holeDatas) {
          h[1].assisObj.setDefaultColor();
        }
        this.illegal = false;
        this.abort();
      }
    }
  };

  pointerMove = (e) => {
    this.pickPoint(e);

    if (this.step === Step.MOVEEND) {
      const point = this.sceneSetup.mouseOnPlane(
        e.mouse.x,
        e.mouse.y,
        this.basePlaneNormal,
        this.ptOnPlane.clone(),
      );
      this.endPoint = new Vector3(point.x, point.y, point.z);
      this.endPoint = this.hoverPointVec
        ? this.hoverPointVec
        : MathTool.moduleProgression(this.startPoint, this.endPoint, 10);
      if (this.startPoint) {
        this.drawLabelLine(e);

        this.calculateTransform();
      }
    }
  };

  keyUp = (e) => {
    if (e.key === KEYBOARD_KEY.SPACE || e.key === KEYBOARD_KEY.ENTER) {
      if (this.step === Step.MOVEEND && this.lineLabel) {
        const text = +this.lineLabel.getText();
        if (text != this.length) {
          this.length = MathTool.fixNumber(text, 6);
          const moveDir = this.endPoint
            .clone()
            .sub(this.startPoint.clone())
            .normalize();
          // 计算偏移矩阵并应用
          this.deltaPosition = moveDir
            .clone()
            .setLength(this.length)
            .add(this.position.clone());
          this.previousPosition = this.deltaPosition.clone();
          this.matrix = new Matrix4().makeTranslation(
            this.deltaPosition.x,
            this.deltaPosition.y,
            this.deltaPosition.z,
          );
          this.finalMatrix = this.matrix
            .clone()
            .multiply(this.previousInverseMatrix.clone());
          Transformer().doTransform(this.holeDatas, this.finalMatrix);
          this.previousInverseMatrix = this.matrix.clone().invert();
          // 计算transform偏移矩阵
          this.deltaOrigin = moveDir.clone().setLength(this.length);
          this.csysMatrix = new Matrix4().makeTranslation(
            this.deltaOrigin.x,
            this.deltaOrigin.y,
            this.deltaOrigin.z,
          );

          // 移动包围盒
          // const boxs = Picker.getSelectedGroupBoxs();
          // Transformer().applyGroupBoxs(boxs, this.finalMatrix);
          this.continues();
        } else {
          this.continues();
        }
      }
    } else if (e.key === KEYBOARD_KEY.ESC) {
      if (this.step === Step.MOVEEND) {
        for (const h of this.holeDatas) {
          h[1].assisObj.setDefaultColor();
        }
        this.illegal = false;
        this.abort();
      } else {
        this.exit();
      }
    }
  };

  enter() {
    csEvent().on(CENTEREVENTS.POINTERDOWN, this.pointerDown);
    csEvent().on(CENTEREVENTS.POINTERUP, this.pointerUp);
    csEvent().on(CENTEREVENTS.POINTERMOVE, this.pointerMove);
    document.addEventListener('keyup', this.keyUp);
    // ctx2.on(CMEVENTS.MODEL_SHAPE_CHANGED, this.updateModels);
    userEvents.on(CMEVENTS.MODEL_PLACEMENT_CHANGED, this.updateModels);
    // 移动洞口时切换命令,退出洞口移动
  }

  exit() {
    csEvent().off(CENTEREVENTS.POINTERDOWN, this.pointerDown);
    csEvent().off(CENTEREVENTS.POINTERUP, this.pointerUp);
    csEvent().off(CENTEREVENTS.POINTERMOVE, this.pointerMove);
    document.removeEventListener('keyup', this.keyUp);
    // ctx2.off(CMEVENTS.MODEL_SHAPE_CHANGED, this.updateModels);
    userEvents.off(CMEVENTS.MODEL_PLACEMENT_CHANGED, this.updateModels);
    this.end();
    MoveHole._instance = null;
    userEvents.emit('exitCmdMove');
  }

  destroy() {
    if (!MoveHole._instance) return;
    csEvent().off(CENTEREVENTS.POINTERDOWN, this.pointerDown);
    csEvent().off(CENTEREVENTS.POINTERUP, this.pointerUp);
    csEvent().off(CENTEREVENTS.POINTERMOVE, this.pointerMove);
    document.removeEventListener('keyup', this.keyUp);
    userEvents.off(CMEVENTS.MODEL_PLACEMENT_CHANGED, this.updateModels);
    this.end();
    MoveHole._instance = null;
  }
}

/**
 * 洞口移动控制器
 */
export const HoleMover = () => {
  return MoveHole.getInstance();
};
